#include "RateController.hh"

namespace vigil
{
	static Vlog_module lg("RateController");
	RateController::RateController(const Context* c, const xercesc::DOMNode*)
		: Component(c) 
	{}

	/** Configure component
	 * Register events.
	 * @param config configuration
	 */
	void RateController::configure(const Configuration* config) 
	{ 
		resolve(routing);
		register_handler<Flow_in_event>
			(boost::bind(&RateController::handle_flow_in, this, _1));

	}

	/** Start component.
	 */
	void RateController::install() 
	{}

	void RateController::getInstance(const container::Context* ctxt,
			RateController*& ofp)
	{
		ofp = dynamic_cast<RateController*>
			(ctxt->get_by_interface(container::Interface_description
						(typeid(RateController).name())));
	}
	Disposition RateController::handle_flow_in(const Event& e)
	{
		const Flow_in_event& fi = assert_cast<const Flow_in_event&>(e);

		Routing_module::RoutePtr route;
		uint16_t inport, outport;
		get_route(fi, route, inport, outport);
		ethernetaddr src_mac = fi.flow.dl_src;
		ethernetaddr dst_mac = fi.flow.dl_dst;
		
		
		
		routing->setup_route(rev_action_flow, *new_branch, new_branch_inport, new_branch_outport, FLOW_TIMEOUT,
				Routing_module::ActionList(0), fi.dst_addr_groups.get(),
				fi.src_addr_groups.get());

	}
	bool 
		RateController::get_route(const Flow_in_event& fi,
				Routing_module::RoutePtr& route,
				uint16_t& inport, uint16_t& outport)
		{
			Routing_module::RoutePtr empty(new Routing_module::Route()); // empty is some junk RoutePtr

			if (fi.src_to_route == NULL) {
				empty->id.src = datapathid::from_host(DP_FROM_AP(fi.src->location));
				inport = (uint16_t)(fi.src->location >> 48);
			} else {
				empty->id.src = datapathid::from_host(DP_FROM_AP(fi.src_to_route->location));
				inport = (uint16_t)(fi.src_to_route->location >> 48);
			}

			const ConnList *dst;
			if (fi.dst_to_route.empty()) {
				dst = &fi.dst;
			} else {
				dst = &fi.dst_to_route;
			}

			if (!dst->empty() && dst->front()->location != 0) {
				bool checked = false;
				for (ConnList::const_iterator iter = dst->begin();
						iter != dst->end(); ++iter)
				{
					bool check = false;
					empty->id.dst = datapathid::from_host(DP_FROM_AP((*iter)->location));
					if (empty->id.src == empty->id.dst) {
						route = empty;
						check = true;
					} else {
						check = routing->get_route(empty->id, route);
					}

					if (check) {
						checked = true;
						outport = (uint16_t) ((*iter)->location >> 48);
						if (routing->check_route(*route, inport, outport)) {
							return true;
						}
						VLOG_DBG(lg, "Invalid route between aps %"PRIx64":%"PRIu16" and %"PRIx64":%"PRIu16".",
								empty->id.src.as_host(), inport, empty->id.dst.as_host(), outport);
					} else {
						VLOG_DBG(lg, "No route found between dps %"PRIx64" and %"PRIx64".",
								empty->id.src.as_host(), empty->id.dst.as_host());
					}
				}

				//   If failed on check_route(), don't route...correct?
				if (checked) {
					VLOG_WARN(lg, "Could not find valid route to any destination.");
					std::ostringstream os;
					os << fi.flow;
					VLOG_WARN(lg, "Dropping %s", os.str().c_str());
					return false;
				}
			}

			return false;
		}
bool 
RateController::add_db(string mac, Routing_module::RoutePtr rptr, Flow& flow)
{
	cout<< "--------------- Add DB -------------\n"
		<< "Host : " << mac << "\n"
		<< "Route " << route_to_string(*rptr) << "\n"
		<< "Flow : " << flow.to_string() << "\n"
		<< "---------------------------------------\n";

	route_db::iterator rit = db.find(mac);
	if(rit == db.end()) {
		route_data rdata;
		rdata.routes.clear();
		rdata.flows.clear();
		rdata.routes.push_back(rptr);
		rdata.flows.push_back(flow);
		rdata.switched.push_back(false);
		rdata.in_transition = false;
		db[mac] = rdata;
		return true;
	}
	else {
		vector<Flow>& fvec = rit->second.flows;
		vector<Routing_module::RoutePtr>& rvec = rit->second.routes;
		for(int i = 0; i < fvec.size(); i++) {
			if(fvec[i].hash_code() == flow.hash_code()) {
				fvec[i] = flow;
				rvec[i] = rptr;
				return true;
			}
		}
		rit->second.routes.push_back(rptr);
		rit->second.flows.push_back(flow);
		rit->second.switched.push_back(false);
		return true;
	}
	return false;
}

};
REGISTER_COMPONENT(vigil::container::Simple_component_factory
		<vigil::RateController>,
		vigil::RateController);
